package com.shanlitech.bluetooth.map;

import android.graphics.Rect;
import android.util.Log;

import com.shanlitech.lbs.map.MapLayer;

public class WindowMapCoordinator {
    private static final String TAG = "Map2D";

    private final MapLayer mMapLayer = new MapLayer();
    private final Point mViewPoint = new Point();          // 地图左上角相对视窗左上角的偏移
    private final Size mMapBaseSize = new Size();          // 原始地图大小，像素
    private final Size mMapSize = new Size();              // 缩放后的地图大小，像素
    private final Size mWindowSize = new Size();           // 视窗大小，像素
    private final Rect mMapRect = new Rect();              // 地图可见部分，像素
    private final Rect mWindowRect = new Rect();           // 窗口绘制区域，像素
    private final float mMeterPixel;
    private float mScaleFactor = 1.f;
    private float mMaxScaleFactor = 5.0f;
    private float mMinScaleFactor = 0.2f;

    public WindowMapCoordinator(MapLayer layer,int width,int height) {
        mMapLayer.copy(layer);
        mMapBaseSize.width = width;
        mMapBaseSize.height = height;
        mMeterPixel = width / mMapLayer.width();

        mMapSize.width = mMapBaseSize.width;
        mMapSize.height = mMapBaseSize.height;

        update();
    }

    public void setViewPoint(int x,int y) {
        mViewPoint.x = x;
        mViewPoint.y = y;
        update();
    }

    public void setMapSize(int width,int height) {
        mMapSize.width = (int)(mMapBaseSize.width * mScaleFactor);
        mMapSize.height = (int)(mMapBaseSize.height * mScaleFactor);
        update();
    }

    public void setWindowSize(int width,int height) {
        mWindowSize.width = width;
        mWindowSize.height = height;
        update();
    }

    public void scroll(int x,int y) {
        mViewPoint.x += x;
        mViewPoint.y += y;
        update();

    }

    public void scale(float factor) {
        mScaleFactor = Math.max(mMinScaleFactor, Math.min(factor * mScaleFactor, mMaxScaleFactor));
        mMapSize.width = (int)(mMapBaseSize.width * mScaleFactor);
        mMapSize.height = (int)(mMapBaseSize.height * mScaleFactor);
        update();
    }

    public float mapToWindowX(double x,double y) {
        float cx = mViewPoint.x + ((float)x - mMapLayer.left) * mMeterPixel * mScaleFactor;
        return cx;
    }

    public float mapToWindowY(double x,double y) {
        float cy = mViewPoint.y + (mMapLayer.top - (float)y) * mMeterPixel * mScaleFactor;
        return cy;
    }

    public float meterToPixels(double meter) {
        return (float)(meter * mMeterPixel * mScaleFactor);
    }

    public double windowToMapX(float x,float y) {
        double mx = ( (x - mViewPoint.x) / mScaleFactor / mMeterPixel ) + mMapLayer.left;
        return mx;
    }

    public double windowToMapY(float x,float y) {
        double my = mMapLayer.top - ( (y - mViewPoint.y) / mScaleFactor / mMeterPixel ) ;
        return my;
    }

    public double pixelsToMeter(float pixels) {
        return ((double)pixels) / mScaleFactor / mMeterPixel;
    }

    public Rect getMapRect() {
        return mMapRect;
    }

    public Rect getWindowRect() {
        return mWindowRect;
    }

    private void update() {
        if( mViewPoint.x < - mMapSize.width ) {
            mViewPoint.x = - mMapSize.width;
        }
        if( mViewPoint.x > mWindowSize.width ) {
            mViewPoint.x = mWindowSize.width;
        }
        if( mViewPoint.y < - mMapSize.height ) {
            mViewPoint.y = - mMapSize.height;
        }
        if( mViewPoint.y > mWindowSize.height ) {
            mViewPoint.y = mWindowSize.height;
        }

        mMapRect.left = (int)((-mViewPoint.x) / mScaleFactor);
        mMapRect.right = (int)((- mViewPoint.x + mWindowSize.width ) / mScaleFactor);
        mMapRect.top = (int)((-mViewPoint.y) / mScaleFactor);
        mMapRect.bottom = (int)((-mViewPoint.y + mWindowSize.height) / mScaleFactor);
        fixRectInContainer(mMapRect,mMapBaseSize.width,mMapBaseSize.height);

        mWindowRect.left = Math.max(0,mViewPoint.x);
        mWindowRect.right = Math.min(mWindowSize.width,mViewPoint.x + mMapSize.width);
        mWindowRect.top = Math.max(0,mViewPoint.y);
        mWindowRect.bottom = Math.min(mWindowSize.height,mViewPoint.y + mMapSize.height);
    }

    private void fixRectInContainer(Rect rect,int width,int height) {
        if( rect.left < 0 ) {
            rect.left = 0;
        } else if( rect.left > width) {
            rect.left = width;
        }
        if( rect.right < 0 ) {
            rect.right = 0;
        } else if( rect.right > width ) {
            rect.right = width;
        }
        if( rect.top < 0 ) {
            rect.top = 0;
        } else if( rect.top > height) {
            rect.top = height;
        }
        if( rect.bottom < 0 ) {
            rect.bottom = 0;
        } else if( rect.bottom > height) {
            rect.bottom = height;
        }

    }

    private void fixRectInContainer(Rect rect,Rect container) {
        if( rect.left < container.left ) {
            rect.left = container.left;
        } else if( rect.left > container.right ) {
            rect.left = container.right;
        }
        if( rect.right < container.left ) {
            rect.right = container.left;
        } else if( rect.right > container.right ) {
            rect.right = container.right;
        }
        if( rect.top < container.top ) {
            rect.top = container.top;
        } else if( rect.top > container.bottom) {
            rect.top = container.bottom;
        }
        if( rect.bottom < container.top ) {
            rect.bottom = container.top;
        } else if( rect.bottom > container.bottom) {
            rect.bottom = container.bottom;
        }
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("ViewPoint=(").append(mViewPoint.x).append(",").append(mViewPoint.y).append(")");
        sb.append(" MapBaseSize=(").append(mMapBaseSize.width).append(",").append(mMapBaseSize.height).append(")");
        sb.append(" MapSize=(").append(mMapSize.width).append(",").append(mMapSize.height).append(")");
        sb.append(" WindowSize=(").append(mWindowSize.width).append(",").append(mWindowSize.height).append(")");
        sb.append(" MapRect=").append(mMapRect);
        sb.append(" WindowRect=").append(mWindowRect);

        return sb.toString();
    }
}
